import json
import time
import re
from typing import Dict, List, Callable

class Node:
    def __init__(self, node_id: int, node_type: str, label: str):
        self.id = node_id
        self.type = node_type
        self.label = label

    def execute(self, context: Dict):
        raise NotImplementedError("Subclasses must implement execute method")

class StartNode(Node):
    def __init__(self, node_id: int, label: str):
        super().__init__(node_id, 'start', label)

    def execute(self, context: Dict):
        print(f"[{self.type.upper()}] {self.label} (ID: {self.id})")
        print("开始执行流程")
        return None

class SetVarNode(Node):
    def __init__(self, node_id: int, label: str, var_name: str, value):
        super().__init__(node_id, 'setVar', label)
        self.var_name = var_name
        self.value = value

    def execute(self, context: Dict):
        print(f"[{self.type.upper()}] {self.label} (ID: {self.id})")
        context[self.var_name] = self.value
        print(f"设置变量 {self.var_name} = {self.value}")
        return None

class WriteNode(Node):
    def __init__(self, node_id: int, label: str, text: str, command: str, params: List[Dict]):
        super().__init__(node_id, 'write', label)
        self.text = text
        self.command = command
        self.params = params

    def execute(self, context: Dict):
        print(f"[{self.type.upper()}] {self.label} (ID: {self.id})")
        formatted_command = self.command
        for param in self.params:
            if param['category'] == 'in':
                value = param.get('value')
                formatted_command = formatted_command.replace(f'{{{param["code"]}}}', str(value))
        print(f"执行写操作 - {self.text}，命令：{formatted_command}")
        return None

class SleepNode(Node):
    def __init__(self, node_id: int, label: str, var_name: str, value: int):
        super().__init__(node_id, 'sleep', label)
        self.var_name = var_name
        self.value = value

    def execute(self, context: Dict):
        print(f"[{self.type.upper()}] {self.label} (ID: {self.id})")
        sleep_time_sec = self.value / 1000
        print(f"休眠 {self.value}ms ({sleep_time_sec}s)")
        time.sleep(sleep_time_sec)
        return None

class ReadNode(Node):
    def __init__(self, node_id: int, label: str, text: str, command: str, params: List[Dict]):
        super().__init__(node_id, 'read', label)
        self.text = text
        self.command = command
        self.params = params

    def execute(self, context: Dict):
        print(f"[{self.type.upper()}] {self.label} (ID: {self.id})")
        formatted_command = self.command
        for param in self.params:
            if param['category'] == 'in':
                value = param.get('value')
                formatted_command = formatted_command.replace(f'{{{param["code"]}}}', str(value))
        voltage = context.get('voltage', 1.5)
        current = context.get('current', 0.5)
        response = f"CH1:{voltage}V/{current}A"
        print(f"执行读操作 - {self.text}，命令：{formatted_command}，模拟响应：{response}")

        for param in self.params:
            if param['category'] == 'out':
                regex = param['regex']
                match = re.search(regex, response)
                if match:
                    value_str = match.group(1)
                    if param['type'] == 'number':
                        value = float(value_str) if '.' in value_str else int(value_str)
                    else:
                        value = value_str
                    context[param['code']] = value
                    print(f"提取输出变量 {param['code']} = {value}（正则匹配：{regex}）")
                else:
                    print(f"警告：无法从响应中提取变量 {param['code']}，正则表达式：{regex}")
        return None

class ChangeVarNode(Node):
    def __init__(self, node_id: int, label: str, var_name: str, value):
        super().__init__(node_id, 'changeVar', label)
        self.var_name = var_name
        self.value = value

    def execute(self, context: Dict):
        print(f"[{self.type.upper()}] {self.label} (ID: {self.id})")
        current_value = context.get(self.var_name, 0)
        new_value = current_value + self.value
        context[self.var_name] = new_value
        print(f"更新变量 {self.var_name}：{current_value} → {new_value}（增量：{self.value}）")
        return None

class EndNode(Node):
    def __init__(self, node_id: int, text: str):
        super().__init__(node_id, 'end', text)

    def execute(self, context: Dict):
        print(f"[{self.type.upper()}] {self.label} (ID: {self.id})")
        print("执行结束节点，流程终止")
        return 'end'

def parse_flow(json_data: Dict) -> (Dict[int, Node], Dict[int, List[Callable[[Dict], bool]]]):
    nodes = {}
    for node_data in json_data['nodes']:
        node_id = node_data['id']
        node_type = node_data['type']
        label = node_data.get('label', '')
        if node_type == 'start':
            node = StartNode(node_id, label)
        elif node_type == 'setVar':
            node = SetVarNode(node_id, label, node_data['varName'], node_data['value'])
        elif node_type == 'write':
            node = WriteNode(
                node_id, label, node_data['text'], node_data['command'], node_data['params']
            )
        elif node_type == 'sleep':
            node = SleepNode(
                node_id, label, node_data.get('varName', ''), node_data['value']
            )
        elif node_type == 'read':
            node = ReadNode(
                node_id, label, node_data['text'], node_data['command'], node_data['params']
            )
        elif node_type == 'changeVar':
            node = ChangeVarNode(
                node_id, label, node_data['varName'], node_data['value']
            )
        elif node_type == 'end':
            node = EndNode(node_id, node_data.get('text', ''))
        else:
            raise ValueError(f"未知节点类型：{node_type}，节点ID：{node_id}")
        nodes[node_id] = node

    edges = {}
    for edge_data in json_data['edges']:
        source_id = edge_data['source']
        target_id = edge_data['target']
        condition_str = edge_data.get('condition')

        if condition_str:
            # 修改：使用 ctx.get() 方法来安全地访问变量，避免 NameError
            try:
                # 替换变量名为 ctx.get() 调用，提供默认值 0
                safe_condition_str = re.sub(r'\b([a-zA-Z_][a-zA-Z0-9_]*)\b', r"ctx.get('\1', 0)", condition_str)
                condition = eval(f"lambda ctx: {safe_condition_str}", {}, {})
            except Exception as e:
                raise ValueError(f"条件表达式解析错误：{condition_str}，错误：{e}")
        else:
            condition = lambda ctx: True

        if source_id not in edges:
            edges[source_id] = []
        edges[source_id].append((target_id, condition))

    return nodes, edges

def execute_flow(nodes: Dict[int, Node], edges: Dict[int, List[Callable[[Dict], bool]]]):
    context = {}
    current_node_id = 1
    step_count = 1

    while True:
        if current_node_id not in nodes:
            raise RuntimeError(f"节点ID {current_node_id} 不存在")

        current_node = nodes[current_node_id]
        print(f"\n{'='*20} 步骤 {step_count}: {current_node.type.upper()} 节点 {'='*20}")
        print(f"当前上下文: {context}")

        result = current_node.execute(context)
        step_count += 1

        if isinstance(current_node, EndNode):
            print(f"\n{'='*20} 流程结束 {'='*20}")
            break

        if current_node_id not in edges:
            raise RuntimeError(f"节点 ID {current_node.id} 没有出边定义")

        next_node_id = None
        for target_id, condition in edges[current_node_id]:
            if condition(context):
                next_node_id = target_id
                # 修改：显示目标节点的名称而不是仅仅显示ID
                next_node_label = nodes[target_id].label if target_id in nodes else "未知节点"
                print(f"条件满足，转移到节点: {next_node_label} (ID: {next_node_id})")
                break

        if next_node_id is None:
            raise RuntimeError(f"节点 ID {current_node.id} 没有满足条件的出边")

        current_node_id = next_node_id

if __name__ == "__main__":
    json_data = {
        "nodes": [
            {"id": 1, "type": "start", "label": "开始"},
            {"id": 2, "type": "setVar", "label": "设置变量 循环次数", "varName": "index", "value": 1},
            {"id": 3, "type": "write", "text": "设置电压电流", "command": ":APPL {{channel}},{{voltage}}", "params": [
                {"category": "in", "code": "channel", "name": "通道", "type": "string", "value": "1"}, 
                {"category": "in", "code": "voltage", "name": "电压", "type": "number", "value": 1.5}, 
                {"category": "in", "code": "current", "name": "电流", "type": "number", "value": 0.5}
            ]},
            {"id": 4, "type": "sleep", "label": "休眠500ms", "varName": "index", "value": 1000},
            {"id": 5, "type": "read", "text": "读取电压电流", "command": ":APPL? {{channel}}", "params": [
                {"category": "in", "code": "channel", "name": "通道", "type": "string", "value": "1"}, 
                {"category": "out", "code": "voltage", "name": "电压", "type": "number", "regex": "CH1:([\\d.]+)V"}, 
                {"category": "out", "code": "current", "name": "电流", "type": "number", "regex": "/([\\d.]+)A"}
            ]},
            {"id": 6, "type": "changeVar", "text": "更新变量 循环次数++", "varName": "index", "value": 1},
            {"id": 7, "type": "end", "text": "结束"}
        ],
        "edges": [
            {"source": 1, "target": 2, "text": ""},
            {"source": 2, "target": 3, "text": ""},
            {"source": 3, "target": 4, "text": ""},
            {"source": 4, "target": 5, "text": ""},
            {"source": 5, "target": 6, "text": ""},
            {"source": 6, "target": 3, "text": "循环次数<=10", "condition": "index<10"},
            {"source": 6, "target": 7, "text": "循环次数>=10", "condition": "index>=10"}
        ]
    }

    nodes, edges = parse_flow(json_data)
    execute_flow(nodes, edges)